﻿/*
 *  Copyright 2010 www.jaxmao.org
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.Xml;
using System.IO;
using System.Collections;

namespace SafeBox
{
    public class CryptionTool
    {
        private const int KEY_SIZE = 256;
        private const int BLOCK_SIZE = 128;

        RijndaelManaged rijndae;

        public CryptionTool()
        {
            this.rijndae = new RijndaelManaged();
            this.rijndae.BlockSize = 128;
            this.rijndae.KeySize = 256;

            this.rijndae.GenerateIV();
        }

        private byte[] IV
        {
            get { return this.rijndae.IV; }
        }

        public byte[] newEncrypKey(string pwdText)
        {
            byte[] secretKey = null;
            if (string.IsNullOrEmpty(pwdText))
            {
            }
            else
            {
                SHA256Managed sha = new SHA256Managed();
                secretKey = sha.ComputeHash(Encoding.UTF8.GetBytes(pwdText));
            }

            return secretKey;
        }

        public void encryptItems(Hashtable items, string outFilePath, byte[] key)
        {

            try
            {
                XmlDocument xmlDoc = new XmlDocument();
                XmlTextWriter xmlWriter = null;
                FileStream stream = null;

                try
                {
                    stream = new FileStream(outFilePath, FileMode.OpenOrCreate, FileAccess.Write);
                    stream.Write(IV, 0, IV.Length);

                    using (CryptoStream csEncrypt = new CryptoStream(stream, this.rijndae.CreateEncryptor(key, IV), CryptoStreamMode.Write))
                    {
                        using (StreamWriter sWriter = new StreamWriter(csEncrypt, Encoding.UTF8))
                        {
                            xmlWriter = new XmlTextWriter(sWriter);
                            xmlWriter.Formatting = Formatting.Indented;
                            xmlWriter.WriteProcessingInstruction("xml", "version='1.0' encoding='UTF-8'");

                            if (items != null && items.Count > 0)
                            {
                                XmlElement root = xmlDoc.CreateElement("Items");
                                foreach (string k in items.Keys)
                                {
                                    XmlElement item = xmlDoc.CreateElement("Item");
                                    XmlElement itemName = xmlDoc.CreateElement("Name");
                                    XmlCDataSection name = xmlDoc.CreateCDataSection(k);
                                    itemName.AppendChild(name);
                                    item.AppendChild(itemName);
                                    XmlElement itemDetail = xmlDoc.CreateElement("Detail");
                                    XmlCDataSection detail = xmlDoc.CreateCDataSection(items[k] as string);
                                    itemDetail.AppendChild(detail);
                                    item.AppendChild(itemDetail);

                                    root.AppendChild(item);
                                }
                                xmlDoc.AppendChild(root);
                                xmlDoc.WriteContentTo(xmlWriter);
                            }
                            else
                            {
                                Console.WriteLine("Nothing to be saved.");
                            }

                            csEncrypt.Flush();

                        }
                    }
                }
                finally
                {
                    if (xmlWriter != null)
                    {
                        xmlWriter.Close();
                    }
                    if (stream != null)
                    {
                        stream.Close();
                    }
                }

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.GetType().Name + ": " + ex.ToString());
            }
        }

        public Hashtable decryptItems(string file, byte[] key)
        {
            Hashtable ret = new Hashtable();
            FileStream infile = null;
            try
            {
                infile = new FileStream(file, FileMode.Open, FileAccess.Read);
                byte[] iv = new byte[BLOCK_SIZE/8];
                infile.Read(iv, 0, BLOCK_SIZE/8);
                ret = this.decryptItems(infile, key, iv);
            }
            finally
            {
                if (infile != null)
                {
                    infile.Close();
                }
            }
            return ret;
        }

        private Hashtable decryptItems(Stream stream, byte[] key, byte[] iv)
        {
            Hashtable ret = null;
            try
            {
                using (CryptoStream csDecrypt = new CryptoStream(stream, this.rijndae.CreateDecryptor(key, iv), CryptoStreamMode.Read))
                {
                    using (StreamReader sReader = new StreamReader(csDecrypt, Encoding.UTF8))
                    {

                        XmlDocument xmldoc = new XmlDocument();

                        ret = new Hashtable();
                        xmldoc.Load(sReader);

                        XmlNodeList list = xmldoc.SelectNodes("Items/Item");

                        foreach (XmlNode node in list)
                        {
                            XmlNodeList children = node.ChildNodes;
                            string n = null;
                            string d = null;
                            foreach (XmlNode child in children)
                            {
                                if (child.Name.Equals("Name"))
                                {
                                    n = child.InnerText;
                                }
                                else if (child.Name.Equals("Detail"))
                                {
                                    d = child.InnerText;
                                    Console.WriteLine(d);
                                }
                            }
                            ret.Add(n, d);
                        }
                    }
                }
            }
            finally
            {
                if (stream != null)
                {
                    stream.Close();
                }
            }

            return ret;

        }
    }
}
